#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <GL/glut.h>

#define VIEWPORT_WIDTH 640
#define VIEWPORT_HEIGHT 480
#define KEY_Q 113

int initConnection(int);
int initGL(void);

GLvoid OnDisplay();
GLvoid OnReshape(GLint w, GLint h);
GLvoid OnKeyPress(unsigned char key, GLint x, GLint y);
GLvoid OnIdle();
void saveImage(IplImage);
void printUsage(void);

GLint g_hWindow;
int newsockfd, sockfd;
CvCapture* g_Capture;
char* dest;

int main(int argc, char *argv[]){
  
  //Parse arguemtns
  if (argc < 3){
    printUsage();
    return 1;
  }

  int portno = atoi(argv[1]);

  char* file = argv[2];

  if(file[strlen(file)-1] != '/'){
    //Cat a / on to the end
    char* new_file = (char*)(malloc(strlen(file)*sizeof(char) +
                                    sizeof(char)));
    strcpy (new_file, file);
    strcat (new_file, "/");
    file = new_file; 
  }

  char* name = (char*)"test.jpg";
  dest = (char*)(malloc(strlen(file)*sizeof(char) +
                              strlen(name)*sizeof(char)));
  strcpy (dest, file);
  strcat (dest, name);
  printf("Saving to %s\n", dest);

  //Note: Will hang on this line until client connects
  if(initConnection(portno) != 0){
    fprintf(stderr, "Unable to init connection.\n");
    return 1;
  }

  printf("Init connection complete.\n");

  //Init GL
  glutInit(&argc, argv);
  if(initGL() != 0){
    fprintf(stderr, "Unable to init GL Component.\n");
    return 1;
  }

  printf("Init GL complete.\n");

  //Run event loop
  glutMainLoop();
  return 0;
}


int initConnection(int portno){
  struct sockaddr_in serv_addr, cli_addr;
  socklen_t clilen;

  sockfd = socket(AF_INET, SOCK_STREAM, 0);

  if (sockfd < 0){
    fprintf(stderr, "Unable to open socket!\n");
    return 1;
  }

  //Zero buffer
  bzero((char*) &serv_addr, sizeof(serv_addr));

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(portno);
  serv_addr.sin_addr.s_addr = INADDR_ANY;

  //Time to Bind
  if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
    fprintf(stderr, "Unable to bind socket.\n");
    return 1;
  }

  //Now time to listen for clients
  listen(sockfd, 5); //5 is number of waiting line
  
  //The system will hang on the following line until it gets a client
  clilen = sizeof(cli_addr);
  newsockfd = accept(sockfd,
		     (struct sockaddr *) &cli_addr,
		     &clilen);

  if(newsockfd < 0){
    fprintf(stderr, "Error on accepting new client.\n");
    return 1;
  }

  //Succesful init
  return 0;
}

int initGL(){
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);

  g_hWindow = glutCreateWindow("2012 Rover Team");

  glClearColor(0.0, 0.0, 0.0, 0.0);

  glutDisplayFunc(OnDisplay);
  glutReshapeFunc(OnReshape);
  glutKeyboardFunc(OnKeyPress);
  glutIdleFunc(OnIdle);

  return 0;
}

GLvoid OnDisplay(){
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glEnable(GL_TEXTURE_2D);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0, VIEWPORT_WIDTH, VIEWPORT_HEIGHT, 0);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  glBegin(GL_QUADS);
  glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f);
  glTexCoord2f(1.0f, 0.0f); glVertex2f(VIEWPORT_WIDTH, 0.0f);
  glTexCoord2f(1.0f, 1.0f); glVertex2f(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
  glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, VIEWPORT_HEIGHT);
  glEnd();

  glFlush();
  glutSwapBuffers();
}

GLvoid OnReshape(GLint w, GLint h)
{
  glViewport(0, 0, w, h);
}

GLvoid OnKeyPress(unsigned char key, int x, int y)
{
  switch(key){
  case KEY_Q:
    glutDestroyWindow(g_hWindow);
    close(newsockfd);
    close(sockfd);
    exit(0);
    break;
  }
  glutPostRedisplay();
}

GLvoid OnIdle()
{
   cv::Mat matrix;

  //First things first, Get the size of the incoming image
  int * size = new int;
  read(newsockfd, size, sizeof(int));
  
  int bytes_read = 0;
  char* message = new char[*size];

  //Need to make sure we read all the bytes
  while(bytes_read < *size){
    int increment = read(newsockfd, message+bytes_read, *size-bytes_read);
    if(increment == -1){
      fprintf(stderr, "Error reading from socket.\n");
      return;
    }
    bytes_read += increment;
  }

  //Convert to a vector in order to convert
  cv::vector<uchar> buf;
  for(int i = 0; i < *size; ++i){
    buf.push_back((uchar)message[i]);
  }

  //Incoming image is in jpg, convert to IPL
  matrix = cv::imdecode(buf, 1);
  IplImage image = matrix;

  //Save image
  saveImage(image);

  //Convert to GL RGB format
  cvCvtColor(&image, &image, CV_BGR2RGB);
  
  gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, image.width, image.height, GL_RGB, GL_UNSIGNED_BYTE, image.imageData);
  
  glutPostRedisplay();
}

void saveImage(IplImage image){
  cvSaveImage(dest, &image);
}

void printUsage(){
  printf("usage: server <portno> <save_loc>\n");
  return;
}
